"""
自动备份工具函数模块

此模块提供了自动备份功能中使用的各种辅助函数和工具方法。
"""

import os
import re
import time
from datetime import datetime, timedelta
from typing import Optional, List, Tuple, Union
from logger_module import log_error, log_warning, log_info


def validate_backup_path(path: str) -> bool:
    """
    验证备份路径的有效性
    
    检查路径是否存在且具有写入权限
    
    Args:
        path: 要验证的路径
    
    Returns:
        bool: 路径是否有效
    """
    if not path:
        log_error("备份路径为空")
        return False
    
    try:
        # 确保路径格式合法
        norm_path = os.path.normpath(path)
        
        # 检查路径是否存在
        if not os.path.exists(norm_path):
            log_error(f"备份路径不存在: {norm_path}")
            return False
        
        # 检查是否是目录
        if not os.path.isdir(norm_path):
            log_error(f"备份路径不是目录: {norm_path}")
            return False
        
        # 双重验证写入权限：先检查access权限
        if not os.access(norm_path, os.W_OK):
            log_error(f"没有写入权限(access检查): {norm_path}")
            return False
        
        # 再通过实际写入测试验证权限
        try:
            # 生成唯一的测试文件名，避免冲突
            test_filename = f"__backup_test_{int(time.time())}.tmp"
            test_file = os.path.join(norm_path, test_filename)
            with open(test_file, 'w') as f:
                f.write("backup_path_test")
            os.remove(test_file)
            log_info(f"备份路径有效且可写: {norm_path}")
            return True
        except PermissionError:
            log_error(f"没有写入权限(写入测试): {norm_path}")
            return False
        except Exception as e:
            log_error(f"写入测试失败: {str(e)}")
            return False
    except Exception as e:
        log_error(f"路径验证过程中发生异常: {str(e)}")
        return False


def generate_backup_filename(db_path: str, prefix: str = 'backup') -> str:
    """
    生成备份文件名
    
    根据数据库文件名和当前时间生成唯一的备份文件名
    
    Args:
        db_path: 数据库文件路径
        prefix: 文件名前缀
    
    Returns:
        str: 生成的备份文件名
    """
    try:
        # 获取数据库文件名（不含扩展名）
        if db_path:
            db_name = os.path.basename(db_path)
            db_name_without_ext = os.path.splitext(db_name)[0]
        else:
            # 如果未提供数据库路径，使用默认名称
            db_name_without_ext = "database"
            log_warning("未提供数据库路径，使用默认名称 'database'")
        
        # 获取当前时间戳
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # 生成备份文件名
        backup_filename = f"{db_name_without_ext}_{prefix}_{timestamp}.db"
        
        log_info(f"生成备份文件名: {backup_filename}")
        return backup_filename
    except Exception as e:
        log_error(f"生成备份文件名时发生错误: {str(e)}")
        # 生成备用文件名
        return f"backup_{int(time.time())}.db"


def ensure_directory_exists(directory: str) -> bool:
    """
    确保目录存在，如果不存在则尝试创建
    
    Args:
        directory: 目录路径
    
    Returns:
        bool: 目录是否存在或创建成功
    """
    if not directory:
        log_error("目录路径为空")
        return False
    
    try:
        # 标准化路径
        norm_path = os.path.normpath(directory)
        
        if not os.path.exists(norm_path):
            log_info(f"创建备份目录: {norm_path}")
            os.makedirs(norm_path, exist_ok=True)
            # 验证目录是否真的被创建
            if not os.path.exists(norm_path):
                log_error(f"目录创建失败: {norm_path}")
                return False
        
        # 验证是否是目录
        if not os.path.isdir(norm_path):
            log_error(f"路径不是目录: {norm_path}")
            return False
            
        log_info(f"成功：目录存在或创建成功: {norm_path}")
        return True
    except PermissionError:
        log_error(f"没有权限创建目录: {directory}")
        return False
    except OSError as e:
        log_error(f"目录创建失败 (OSError): {str(e)}")
        return False
    except Exception as e:
        log_error(f"目录操作过程中发生异常: {str(e)}")
        return False


def get_safe_path(path: str, default_path: str) -> str:
    """
    获取安全的路径，如果原路径无效则返回默认路径
    
    Args:
        path: 原始路径
        default_path: 默认路径
    
    Returns:
        str: 有效的路径
    """
    if validate_backup_path(path):
        return path
    return default_path


def format_backup_status(enabled: bool, frequency: str, backup_time: str) -> str:
    """
    格式化备份状态文本
    
    Args:
        enabled: 是否启用
        frequency: 备份频率
        backup_time: 备份时间
    
    Returns:
        str: 格式化的状态文本
    """
    if enabled:
        return f"自动备份状态: 已启用 ({frequency} {backup_time})"
    else:
        return "自动备份状态: 未启用"


def parse_time_string(time_str: str) -> Optional[Tuple[int, int]]:
    """
    解析时间字符串为小时和分钟
    
    Args:
        time_str: 格式为'HH:MM'的时间字符串
    
    Returns:
        Optional[Tuple[int, int]]: 成功返回(小时, 分钟)元组，失败返回None
    """
    if not time_str:
        return None
    
    try:
        parts = time_str.split(':')
        if len(parts) != 2:
            log_error(f"时间格式无效: {time_str}")
            return None
        
        hour, minute = int(parts[0]), int(parts[1])
        
        if not (0 <= hour <= 23 and 0 <= minute <= 59):
            log_error(f"时间超出有效范围: {time_str}")
            return None
        
        return (hour, minute)
    except ValueError as e:
        log_error(f"时间格式解析错误: {str(e)}")
        return None
    except Exception as e:
        log_error(f"解析时间字符串时发生错误: {str(e)}")
        return None


def get_backup_file_size(backup_path: str) -> Optional[int]:
    """
    获取备份文件的大小（字节）
    
    Args:
        backup_path: 备份文件路径
    
    Returns:
        int: 文件大小（字节），如果文件不存在则返回None
    """
    try:
        if os.path.exists(backup_path) and os.path.isfile(backup_path):
            return os.path.getsize(backup_path)
        return None
    except Exception:
        return None


def format_file_size(size_bytes: int) -> str:
    """
    格式化文件大小为人类可读的形式
    
    Args:
        size_bytes: 文件大小（字节）
    
    Returns:
        str: 格式化的文件大小
    """
    for unit in ['B', 'KB', 'MB', 'GB']:
        if size_bytes < 1024.0:
            return f"{size_bytes:.2f} {unit}"
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} TB"


def cleanup_old_backups(directory: str, max_backups: int = 10) -> int:
    """
    清理旧的备份文件，只保留最新的指定数量的备份
    
    Args:
        directory: 备份目录
        max_backups: 最大保留的备份数量
    
    Returns:
        int: 成功删除的文件数量
    """
    try:
        if not directory:
            log_error("目录路径为空")
            return 0
            
        if not validate_backup_path(directory):
            log_error(f"备份目录无效: {directory}")
            return 0
            
        if max_backups < 1:
            log_warning("保留数量必须大于0，设置为默认值10")
            max_backups = 10
            
        # 获取目录中的所有.db备份文件，扩展匹配模式
        backup_files = []
        for filename in os.listdir(directory):
            # 支持多种备份文件命名模式
            is_backup_file = (filename.endswith('.db') and 
                             ('_backup_' in filename or 
                              '_auto_backup_' in filename or 
                              filename.startswith('backup_')))
            
            if is_backup_file:
                file_path = os.path.join(directory, filename)
                try:
                    # 确保是文件而不是目录
                    if os.path.isfile(file_path):
                        # 获取文件的修改时间
                        mtime = os.path.getmtime(file_path)
                        backup_files.append((file_path, mtime))
                except Exception as e:
                    log_warning(f"无法获取文件信息 {file_path}: {str(e)}")
                    continue
        
        # 按修改时间排序（最新的在前）
        backup_files.sort(key=lambda x: x[1], reverse=True)
        
        # 记录总备份文件数
        total_backups = len(backup_files)
        log_info(f"找到 {total_backups} 个备份文件，需要保留 {max_backups} 个")
        
        # 删除超过数量限制的旧备份
        files_to_delete = backup_files[max_backups:]
        deleted_count = 0
        failed_count = 0
        
        for file_path, _ in files_to_delete:
            try:
                os.remove(file_path)
                deleted_count += 1
                log_info(f"已删除旧备份文件: {file_path}")
            except Exception as e:
                failed_count += 1
                log_error(f"删除文件失败 {file_path}: {str(e)}")
        
        log_info(f"备份清理完成: 删除 {deleted_count} 个文件, 失败 {failed_count} 个")
        return deleted_count
        
    except Exception as e:
        log_error(f"清理旧备份过程中发生错误: {str(e)}")
        return 0


def is_valid_frequency(frequency: str) -> bool:
    """
    验证备份频率是否有效
    
    Args:
        frequency: 备份频率字符串
    
    Returns:
        bool: 频率是否有效
    """
    valid_frequencies = ['每天', '每周', '每月']
    result = frequency in valid_frequencies
    
    if not result:
        log_error(f"无效的备份频率: {frequency}, 有效值为: {', '.join(valid_frequencies)}")
    
    return result


def get_next_backup_time(frequency: str, backup_time: str) -> Optional[datetime]:
    """
    计算下一次备份的时间
    
    Args:
        frequency: 备份频率
        backup_time: 备份时间（HH:MM格式）
    
    Returns:
        datetime: 下一次备份的时间，如果计算失败则返回None
    """
    try:
        # 验证输入参数
        if not is_valid_frequency(frequency):
            log_warning(f"无效的备份频率 '{frequency}'，使用默认频率 '每天'")
            frequency = '每天'
        
        # 解析备份时间
        time_parts = parse_time_string(backup_time)
        if not time_parts:
            log_warning(f"无效的备份时间 '{backup_time}'，使用默认时间 '00:00'")
            hour, minute = 0, 0
        else:
            hour, minute = time_parts
        
        # 获取当前时间
        now = datetime.now()
        
        # 设置下一次备份的时间
        next_backup = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
        
        # 根据频率调整日期
        if frequency == '每天':
            # 如果今天的备份时间已过，则安排到明天
            if next_backup <= now:
                next_backup += timedelta(days=1)
                log_info(f"设置下次备份时间（每天）: {next_backup}")
            else:
                log_info(f"今日备份时间未到，下次备份时间: {next_backup}")
        
        elif frequency == '每周':
            # 每周一执行备份
            days_until_monday = (7 - now.weekday()) % 7
            if days_until_monday == 0:
                # 今天是周一，但如果备份时间已过，则安排到下周一
                if next_backup <= now:
                    days_until_monday = 7
            next_backup = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
            next_backup += timedelta(days=days_until_monday)
            log_info(f"设置下次备份时间（每周）: {next_backup}")
        
        elif frequency == '每月':
            # 更健壮的每月备份处理
            # 先尝试设置为本月的目标日期
            try:
                # 保留当前日，除非它大于当前月的天数
                target_day = now.day
                # 检查今天是否是目标日期且备份时间未过
                if target_day == now.day and next_backup > now:
                    # 今天执行
                    pass
                else:
                    # 准备下个月
                    if now.month == 12:
                        next_year = now.year + 1
                        next_month = 1
                    else:
                        next_year = now.year
                        next_month = now.month + 1
                    
                    # 处理月末日期问题，确保日期有效
                    # 先尝试使用28号作为安全日期
                    safe_day = min(target_day, 28)
                    next_backup = datetime(next_year, next_month, safe_day, hour, minute)
                    
                    # 尝试恢复原日期（如果有效）
                    try:
                        next_backup = datetime(next_year, next_month, target_day, hour, minute)
                    except ValueError:
                        # 如果原日期在目标月无效，保持使用安全日期
                        pass
            except Exception as e:
                log_warning(f"设置每月备份时间时出错: {str(e)}")
                # 回退到下月1号
                if now.month == 12:
                    next_backup = datetime(now.year + 1, 1, 1, hour, minute)
                else:
                    next_backup = datetime(now.year, now.month + 1, 1, hour, minute)
                    
            log_info(f"设置下次备份时间（每月）: {next_backup}")
        
        return next_backup
        
    except Exception as e:
        log_error(f"计算下次备份时间时发生错误: {str(e)}")
        # 返回默认值：当前时间的下一天的同一时间
        return now.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)